﻿#region Copyright(c) 1998-2012, Arnaud Colin Licence GNU GPL version 3
/* Copyright(c) 1998-2012, Arnaud Colin
 * All rights reserved.
 *
 * Licence GNU GPL version 3
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   -> Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   -> Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
#if NET35
using System.Linq;
#endif
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;



namespace XoLib.Tools
{

    /// <summary>
    /// Interface ILog
    /// </summary>
    public interface ILog
    {
        /// <summary/>
        void Write();
    }

    /// <summary>
    /// Interface IActionLog To create a class action to write log data to a media
    /// </summary>
    public interface IActionLog
    {
        /// <summary>
        /// Method call by the .Write();
        /// </summary>
        void Write(LogEntry Entry);
    }

    /// <summary>
    /// Different kind of error
    /// </summary>
    public enum LogEntryType
    {
        /// <summary>
        /// Error
        /// </summary>
        Error = 1,

        /// <summary>
        /// Warning
        /// </summary>
        Warning = 2,

        /// <summary>
        /// Information
        /// </summary>
        Information = 4,
    }


    /// <summary>
    /// LogEntry All the data
    /// </summary>
    public class LogEntry : XoLib.Serialization.DocXML
    {

        /// <summary>
        /// element
        /// </summary>
        protected const string NodeName = "element";
        /// <summary>
        /// Key of the Xml Document
        /// </summary>
        protected const string NodeRoot = "root";
        /// <summary>
        /// Structure of the Xml Document
        /// </summary>
        protected const string DocumentRoot = "<{0} Date='{1}' >  </{0}>";
        /// <summary>
        /// DateTime Format
        /// </summary>
        protected const string DateRoot = "yyyy/MM/dd HH:mm:ss";

    }


    /// <summary>
    /// CLogger Replace T by a Class Action like CLogFile to Write in a File
    /// <example> CLogger CLogFile .WriteEntry(250,LogEntryType.Error,"A Error").Where().Write(); </example>
    /// </summary>
    [Serializable]
    public partial class CLogger<T> : LogEntry, IDisposable , ILog where T : IActionLog, new() 
    {

        /// <summary>
        /// Constructor
        /// </summary>
        public CLogger()
        {
            Doc.LoadXml(string.Format(DocumentRoot, NodeRoot, DateTime.Now.ToString(DateRoot)));
        }

        /// <summary>
        /// Call Write() of the T Class Action
        /// </summary>
        public void Write()
        {
            var ClassAction = new T();
            ClassAction.Write(this);
            this.Dispose();

        }

        /// <summary>
        /// Dispose
        /// </summary>
        public void  Dispose()
        {
        }

        /// <summary>
        /// <para>
        /// <param name="ex">Exception</param>
        /// </para>
        /// Create instance and write Exception information
        /// <example> CLogger CLogFile .WriteException( new Exception("Sample") ).Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public static CLogger<T> WriteException(Exception ex)
        {
            return Open().CallException(ex);
        }

        /// <summary>
        /// <para>
        /// <param name="mes">string</param>
        /// </para>
        /// Create instance and write the string mes
        /// <example> CLogger CLogFile .WriteMessage("Sample").Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public static CLogger<T> WriteMessage(string mes)
        {
            return Open().CallMessage(mes);
        }

        /// <summary>
        /// <para>
        /// <param name="Code">string</param>
        /// <param name="EntryType">params</param>
        /// <param name="Texte">string Optional</param>
        /// </para>
        /// Create instance and write entry code, the type of event and a text
        /// <example> CLogger CLogFile .WriteEntry(250,LogEntryType.Error,"A Error").Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
#if !WindowsCE
        public static CLogger<T> WriteEntry(int Code, LogEntryType EntryType, string Texte = null)
#else
        public static CLogger<T> WriteEntry(int Code, LogEntryType EntryType, string Texte )
#endif
        {
            return Open().CallEntry(Code, EntryType, Texte);
        }

        /// <summary>
        /// Create instance
        /// <example> var log = CLoggerCLogFile.Open(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public static CLogger<T> Open()
        {
            return new CLogger<T>();
        }

        /// <summary>
        /// Write version in Xml
        /// <example> CLogger CLogFile .Open().Version().Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
        public CLogger<T> Version()
        {
             XmlNode xMaitre = Doc.CreateNode(NodeName, "Version", "");
             xMaitre.InnerText = CVersion.GetInfo().ToString(); ;

             XmlElement root = Element;
             root.AppendChild(xMaitre);

            return this;
         
        }

        /// <summary>
        /// <para>
        /// <param name="ex">string</param>
        /// </para>
        /// Write Message in Xml
        /// <example> CLoggerCLogFile.Open().CallMessage("Sample").Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
        public CLogger<T> CallMessage(string ex)
        {
            XmlNode xMaitre = Doc.CreateNode(NodeName, "Entry", "");
            xMaitre.InnerText = ex;

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);

            return this;
        }

        /// <summary>
        /// <para>
        /// <param name="code">int</param>
        /// <param name="type">LogEntryType</param>
        /// <param name="Texte">string</param>
        /// </para>
        /// Write Entry invormation in Xml
        /// <example> CLoggerCLogFile.Open().CallMessage("Sample").Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
        public CLogger<T> CallEntry(int code, LogEntryType type, string Texte)
        {
            XmlNode xMaitre = Doc.CreateNode(NodeName, "Entry", "");
            if(Texte != null)
                xMaitre.InnerText = Texte;

            XmlAttribute xAttribute = Doc.CreateAttribute("Code");
            xAttribute.Value = code.ToString();
            xMaitre.Attributes.Append(xAttribute);

            xAttribute = Doc.CreateAttribute("Type");
            xAttribute.Value = ((int)type).ToString();
            xMaitre.Attributes.Append(xAttribute);

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);

            return this;
        }


        /// <summary>
        /// <para>
        /// <param name="ex">Exception</param>
        /// </para>
        /// Read data in the Xml
        /// <example> CLoggerCLogFile.Open().CallException(new Exception("Sample")).Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
        public CLogger<T> CallException(Exception ex)
        {
            XmlNode xMaitre = Doc.CreateNode(NodeName, "Exception", "");

            AddChild(xMaitre, "Message", ex.Message);
            AddChild(xMaitre, "StackTrace", ex.StackTrace);
#if !WindowsCE
            AddChild(xMaitre, "Source", ex.Source);
            AddChild(xMaitre, "TargetSite", ex.TargetSite);
#endif

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);

            return this;
        }


        /// <summary>
        /// <para>
        /// <param name="Node">XmlNode</param>
        /// <param name="Titre">string</param>
        /// <param name="Texte">string</param>
        /// </para>
        /// Create a Attribute in the XmlNode
        /// <example>  </example>/// 
        /// <returns>XmlAttribute</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
#if !WindowsCE      
        protected XmlAttribute AddAttribute(XmlNode Node, string Titre, string Texte = null)
#else
        protected XmlAttribute AddAttribute(XmlNode Node, string Titre, string Texte)
#endif
        {

            XmlAttribute xAttribute = Doc.CreateAttribute(Titre);
            xAttribute.Value = Texte;
            Node.Attributes.Append(xAttribute);

            return xAttribute;
        }


        /// <summary>
        /// <para>
        /// <param name="Titre">string</param>
        /// <param name="Texte">params</param>
        /// </para>
        /// Create a top level of the xml node
        /// <example>  </example>/// 
        /// <returns>XmlNode</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
#if !WindowsCE  
        protected XmlNode AddMaitre( string Titre, string Texte = null)
#else
        protected XmlNode AddMaitre( string Titre, string Texte)
#endif
        {

            XmlNode xMaitre = Doc.CreateNode(NodeName, Titre, "");
            if (Texte != null)
                xMaitre.InnerText = Texte;


            return xMaitre;        
        }

        /// <summary>
        /// <para>
        /// <param name="xMaitre">XmlNode</param>
        /// <param name="Titre">string</param>
        /// <param name="Text">string</param>
        /// </para>
        /// Create a child level of the xml node
        /// <example> AddChild(xMaitre, "Message", ex.Message); </example>/// 
        /// <returns>XmlNode</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        protected XmlNode AddChild(XmlNode xMaitre, string Titre, string Text)
        {
            if ((Text != null) && (Text != string.Empty))
            {
                XmlNode xchild = Doc.CreateNode(NodeName, Titre, "");
                xchild.InnerText = Text;
                xMaitre.AppendChild(xchild);
                return xchild;
            }
            return null;
        }

        /// <summary>
        /// <para>
        /// <param name="xMaitre">XmlNode</param>
        /// <param name="Titre">string</param>
        /// <param name="Text">MethodBase</param>
        /// </para>
        /// Create a child level of the xml node
        /// <example> AddChild(xMaitre, "TargetSite", ex.TargetSite); </example>/// 
        /// <returns>XmlNode</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        protected XmlNode AddChild(XmlNode xMaitre, string Titre, MethodBase Text)
        {
            
            if ((Text != null) )
            {
                XmlNode xchild = Doc.CreateNode(NodeName, Titre, "");
                xchild.InnerText = "";

                XmlNode xchild2 = Doc.CreateNode(NodeName, "Method", "");
                xchild2.InnerText = Text.MemberType.ToString();

                XmlNode xchild3 = Doc.CreateNode(NodeName, "Name", "");
                xchild3.InnerText = Text.Name;

                xchild.AppendChild(xchild2);
                xchild.AppendChild(xchild3);
                xMaitre.AppendChild(xchild);
                return xchild;
            }
            return null;
        }

        /// <summary>
        /// <para>
        /// <param name="nom">string</param>
        /// <param name="instance">U</param>
        /// </para>
        /// Write variable information in then xml
        /// <example> CLogger CLogFile .Open().Variable int("i", i).Write();  </example>
        /// <returns>CLogger</returns>
        /// </summary>
        public CLogger<T> Variable<U>(string nom, U instance)
        {

            XmlNode xMaitre = Doc.CreateNode(NodeName, "Variable", "");
            xMaitre.InnerText = typeof(U).ToString();
            XmlAttribute xAttribute = Doc.CreateAttribute(nom);

            if (instance != null)
                xAttribute.Value = instance.ToString();
            else
                xAttribute.Value = "null";

            xMaitre.Attributes.Append(xAttribute);

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);

            return this;
        }

        /// <summary>
        /// <para>
        /// <param name="Method">[CallerMemberName] string</param>
        /// <param name="sourceFilePath">[CallerFilePath] string</param>
        /// <param name="sourceLineNumber">[CallerLineNumber] int</param>
        /// </para>
        /// Write debug information in the xml (Automatique only in NET4.5)
        /// <example> CLogger CLogFile .Open().Where().Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
#if NET45
        public CLogger<T> Where([CallerMemberName] string Method = "", 
                                [CallerFilePath] string sourceFilePath = "", 
                                [CallerLineNumber] int sourceLineNumber = 0)
#else
#if !WindowsCE
        public CLogger<T> Where(string Method = "",
                                string sourceFilePath = "",
                                int sourceLineNumber = 0)
#else
         public CLogger<T> Where( string Method ,
                                string sourceFilePath ,
                                int sourceLineNumber)
#endif
#endif
        {
            XmlNode xMaitre = Doc.CreateNode(NodeName, "Where", "");
            XmlNode xchild1 = Doc.CreateNode(NodeName, "Method", "");
            xchild1.InnerText = Method;

            XmlNode xchild2 = Doc.CreateNode(NodeName, "FilePath", "");
            xchild2.InnerText = sourceFilePath;

            XmlAttribute xAttribute = Doc.CreateAttribute("LineNumber");
            xAttribute.Value = sourceLineNumber.ToString();

            xchild2.Attributes.Append(xAttribute);

            xMaitre.AppendChild(xchild1);
            xMaitre.AppendChild(xchild2);

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);
            return this;
        }


        /// <summary>
        /// <para>
        /// <param name="Method">string</param>
        /// </para>
        /// Write debug information in the xml 
        /// <example> CLogger CLogFile.Open().Where("KeyDown").Write(); </example>/// 
        /// <returns>CLogger</returns>
        /// </summary>
     public CLogger<T> Where( string Method)
        {
            XmlNode xMaitre = Doc.CreateNode(NodeName, "Where", "");
            XmlNode xchild1 = Doc.CreateNode(NodeName, "Method", "");
            xchild1.InnerText = Method;

            xMaitre.AppendChild(xchild1);

            XmlElement root = Doc.DocumentElement;
            root.AppendChild(xMaitre);
            return this;
        }


    }
   
}
